package daobeauty.interceptor.invoke.parser;

import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import daobeauty.interceptor.invoke.parser.operator.AndOperator;
import daobeauty.interceptor.invoke.parser.operator.BetweenOperator;
import daobeauty.interceptor.invoke.parser.operator.EqOperator;
import daobeauty.interceptor.invoke.parser.operator.GeOperator;
import daobeauty.interceptor.invoke.parser.operator.GtOperator;
import daobeauty.interceptor.invoke.parser.operator.ILikeOperator;
import daobeauty.interceptor.invoke.parser.operator.IsNotNullOperator;
import daobeauty.interceptor.invoke.parser.operator.IsNullOperator;
import daobeauty.interceptor.invoke.parser.operator.LeOperator;
import daobeauty.interceptor.invoke.parser.operator.LikeOperator;
import daobeauty.interceptor.invoke.parser.operator.LtOperator;
import daobeauty.interceptor.invoke.parser.operator.NeOperator;
import daobeauty.interceptor.invoke.parser.operator.NotOperator;
import daobeauty.interceptor.invoke.parser.operator.Operator;
import daobeauty.interceptor.invoke.parser.operator.OrOperator;

/**
 * Created 13.09.2008
 * @author M. Serhat Cinar
 *
 */
public class Parser {
	private static final Log LOG = LogFactory.getLog(Parser.class);
	public static final Pattern PATTERN_CAMELCASE = Pattern.compile("([A-Z_][a-z0-9_]*)");
	
	private Set<String> properties;
	private String token;
	private final ParserState parserState = new ParserState();
	private final LinkedHashSet<String> parseErrors = new LinkedHashSet<String>();
	private String input;
	private boolean firstProperty = true;
	
	public Parser(String input, Set<String> properties, Class [] args){
		if (LOG.isDebugEnabled()){
			LOG.debug("----------------------------");
		}
		this.properties = properties;
		if (properties==null || properties.size()<1){
			throw new IllegalArgumentException("set property properties may not be null or empty");
		}
		if (LOG.isDebugEnabled()){
			if (properties==null){
				LOG.debug("properties: null");
			}
			else{
				final StringBuilder sb = new StringBuilder();
				final Iterator<String> propertiesIter = properties.iterator();
				while (propertiesIter.hasNext()){
					sb.append(propertiesIter.next());
					if (propertiesIter.hasNext()){
						sb.append(", ");
					}
				}
				LOG.debug("properties: "+sb.toString());
			}
		}
		if (args!=null){
			for (int i=0; i<args.length; i++){
				parserState.pushArgument(args[i]);
			}
		}
		if (LOG.isDebugEnabled()){
			LOG.debug("argumentStack: "+parserState.getArgumentStack().toString());
		}
		// get all tokens
		if (input!=null && input.trim().length()>0){
			this.input = input;
			final Matcher matcher = PATTERN_CAMELCASE.matcher(input.substring(0, 1).toUpperCase()+input.substring(1));
			while (matcher.find()){
				parserState.getTokenStack().addFirst(matcher.group(1));
			}
		}
		if (LOG.isDebugEnabled()){
			final Iterator<String> tokenIter = parserState.getTokenStack().iterator();
			final StringBuilder sb = new StringBuilder();
			while (tokenIter.hasNext()){
				sb.append(tokenIter.next());
				if (tokenIter.hasNext()){
					sb.append(", ");
				}
			}
			LOG.debug("tokens: "+sb.toString());
		}
	}
	
    private String value() {
		if (LOG.isDebugEnabled()){
			LOG.debug("value(): "+token);
		}

        return token;
    }
    
    private void match(){
		if (LOG.isDebugEnabled()){
			LOG.debug("match(): "+token);
		}
        token = parserState.popToken();
    }

    private void match(String token){
		if (LOG.isDebugEnabled()){
			LOG.debug("match("+token+")");
		}

    	if (this.token.equalsIgnoreCase(token)){
    		if (parserState.getTokenStack().isEmpty()){
    			this.token = null;
    		}
    		else{
    			this.token = parserState.popToken();
    		}
    	}
    	else{
    		throw new ParseException("expected token was \""+token+"\" but found \""+this.token+"\"");
    	}
    }

    
    private void matchEof(){
		if (LOG.isDebugEnabled()){
			LOG.debug("matchEof()");
		}

    	if (!parserState.getTokenStack().isEmpty()){
    		throw new ParseException("Unexpected symbol \""+parserState.getTokenStack().peek()+"\" instead of EOF");
    	}
    }
    
    public Operator parse(){
    	if (parserState.getTokenStack().isEmpty()){
    		throw new UnexpectedEndOfInputException("Empty method name found");
    	}
    	try{
			if (LOG.isDebugEnabled()){
				LOG.debug("parse(): "+input);
			}
			
			property(0);
	    	matchEof();
	    	return parserState.getOperatorStack().pop();
    	}
    	catch (RollBackException e) {
    		parseErrors.add(e.getMessage());
    		final StringBuilder sb = new StringBuilder();
    		final Iterator<String> iter = parseErrors.iterator();
    		while (iter.hasNext()){
    			sb.append(iter.next());
    			if (iter.hasNext()){
    				sb.append(" or ");
    			}
    		}
    		throw new ParseException(sb.toString());
    	}
    	catch(RuntimeException e){
    		logState();
    		throw e;
    	}
    }
    
    
    
    
    
    // DECENT METHODS
    
//    private String findNextProperty(String prefix){
//    	final StringBuilder property = new StringBuilder();
//    	if (prefix!=null){
//    		property.append(prefix);
//    	}
//    	while (value()!=null && !properties.contains(property.toString())){
//    		if (LOG.isDebugEnabled()){
//    			LOG.debug("property in while is "+property);
//    		}
//     		property.append(value());
//    		if (Character.isUpperCase(property.charAt(0))){
//    			property.setCharAt(0, Character.toLowerCase(property.charAt(0)));
//    		}
//        	match();
//    	}
//    	return property.toString();	
//    }
        
    private void property(int tokensToConsumeAtLeast) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*property("+tokensToConsumeAtLeast+")*");
			logState("BEFORE");
		}
		parserState.begin();
		boolean wasFirstProperty = false;
		if (LOG.isDebugEnabled()){
			LOG.debug("firstProperty: "+firstProperty);
		}
		if (firstProperty){
			match();
			firstProperty = false;
			wasFirstProperty = true;
		}
		if (tokensToConsumeAtLeast>parserState.getTokenStack().size()){
			if (LOG.isDebugEnabled()){
				LOG.debug("tokensToConsumeAtLeast("+tokensToConsumeAtLeast+")>available tokens("+parserState.getTokenStack().size()+")");
			}
			final StringBuilder prop = new StringBuilder();
			while (value()!=null){
				prop.append(value());
	    		if (Character.isUpperCase(prop.charAt(0))){
	    			prop.setCharAt(0, Character.toLowerCase(prop.charAt(0)));
	    		}
	        	match();
			}
			throw new UnknownPropertyException("Unknown property "+prop.toString());
		}
		
		int consumedTokens = 0;
		final StringBuilder property = new StringBuilder();
    	while (consumedTokens<tokensToConsumeAtLeast || (value()!=null && !properties.contains(property.toString()))){
    		if (LOG.isDebugEnabled()){
    			LOG.debug("consuming token "+consumedTokens+" property in while is "+property);
    		}
     		property.append(value());
     		consumedTokens++;
    		if (Character.isUpperCase(property.charAt(0))){
    			property.setCharAt(0, Character.toLowerCase(property.charAt(0)));
    		}
        	match();
    	}
		
		if (!properties.contains(property.toString())){
			parserState.rollback();
			if (wasFirstProperty){
				throw new UnknownPropertyException("Unknown property "+property.toString());
			}
			else{
				throw new RollBackException("Unknown property "+property.toString());
			}
    	}
	    
		parserState.pushProperty(property.toString());
	    try{
	    	hqlExpression();
	    }
	    catch (RollBackException e){
	    	parserState.rollback();
	    	logState("AFTER ROLLBACK IN PROPERTY");
	    	if (wasFirstProperty){
	    		firstProperty = true;
	    	}
	    	parseErrors.add(e.getMessage());
	    	property(consumedTokens+1);
		}
    }
    
    // einem hql expression kann ein And Or folgen, dann ein property
    private void hqlExpression() throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*hqlExpression()*");
			logState("BEFORE");
		}
		parserState.begin();
		
		// Input has reached end
		if (value()==null){ // tokenstack is empty
			if (!parserState.getPropertyStack().isEmpty()){
				final String property = parserState.popProperty();
				if (parserState.getArgumentStack().isEmpty()){
					throw new MissingArgumentForOperationException("Missing argument for property "+property);
				}
				else{
					parserState.pushOperator(new EqOperator(property, parserState.popArgument("")));
					return;
				}
			}
			else{
				return; // fini
				// throw new ParseException("No property found");
			}
		}
//    	if (value()==null && tokenStack.isEmpty()){
//    		if (LOG.isDebugEnabled()){
//    			LOG.debug("  tokenStack is empty and value is null");
//    		}
//    		if (propertyStack.isEmpty()){
//        		if (LOG.isDebugEnabled()){
//        			LOG.debug("  propertyStack is empty, too");
//        		}
//    			return;
//    		}
//    		final Class argument = argument("");
//    		if (argument==null){
//        		if (LOG.isDebugEnabled()){
//        			LOG.debug("  argument is empty, will use IsNullOperator");
//        		}
//    			pushOperator(new IsNullOperator(popProperty()));
//    		}
//    		else{
//    			pushOperator(new EqOperator(popProperty(), argument));
//    		}
//    		if (LOG.isDebugEnabled()){
//    			LOG.debug("  tokenStack is empty -> eq: "+operatorStack.peek());
//    		}
//    		return;
//    	}
    	final String value = value().toLowerCase();
    	boolean ok = false;
    	// And
    	// NameAndAddress
    	// NameLikeAndAddress
    	// NameLikeAndNotLike <- zu kompliziert
    	try{
	    	// Like, LikeLower, LikeUpper
	    	if ("like".equals(value)){
	    		like("");
	    		ok = true;
	    	}
	    	// Ilike
	    	else if ("ilike".equals(value)){
	    		ilike("");
	    		ok = true;
	    	}
	    	// I
	    	// ILike
	    	else if("i".equals(value)){
	    		i_like("");
	    		ok = true;
	    	}
	    	// Between
	    	else if ("between".equals(value)){
	    		between("");
	    		ok = true;
	    	}
	    	// Gt
	    	else if ("gt".equals(value)){
	    		gt("");
	    		ok = true;
	    	}
	    	// Lt
	    	else if ("lt".equals(value)){
	    		lt("");
	    		ok = true;
	    	}
	    	// Is
	    	// IsNull, IsNotNull
	    	else if ("is".equals(value)){
	    		is();
	    		ok = true;
	    	}
	    	// Le
	    	else if ("le".equals(value)){
	    		le("");
	    		ok = true;
	    	}
	    	// Ge
	    	else if ("ge".equals(value)){
	    		ge("");
	    		ok = true;
	    	}
	    	// GreaterThan, GreaterThanOrEqual
	    	else if ("greater".equals(value)){
	    		greater("");
	    		ok = true;
	    	}
	    	// LessThan, LessThanOrEqual
	    	else if ("less".equals(value)){
	    		less("");
	    		ok = true;
	    	}
	    	else if ("ne".equals(value)){
	    		ne();
	    		ok = true;
	    	}
	    	else if ("not".equals(value)){
	    		not("");
	    		ok = true;
	    	}
	    	else if ("and".equals(value)){
	    		and();
	    		ok = true;
	    	}
	    	else if ("or".equals(value)){
	    		or();
	    		ok = true;
	    	}
	    	else if ("isnull".equals(value)){
	    		isnull();
	    		ok = true;
	    	}
	    	else if ("isnotnull".equals(value)){
	    		isnotnull();
	    		ok = true;
	    	}
	
	    	if (ok){
	    		if ("and".equalsIgnoreCase(value())){
	    			and();
	    		}
	    		else if ("or".equalsIgnoreCase(value())){
	    			or();
	    		}
	    		return;
	    	}
	    	parserState.rollback();
	    	throw new RollBackException("Unknown operator: "+value+", expected was one of Like, Ilike, I (for ILike), Between, Gt, Lt, Is (for IsNull / IsNotNull), Le, Ge, Greater, Less, Ne, Not, And, Or, Isnull, Isnotnull");
//	    	rollback(value, "Missing one of operations: Like, Ilike, I (for ILike), Between, Gt, Lt, Is (for IsNull / IsNotNull), Le, Ge, Greater, Less, Ne, Not, And, Or, Isnull, Isnotnull");
    	}
    	catch (RollBackException e){
    		parserState.rollback();
    		parseErrors.add(e.getMessage());
    		throw e;
    	}
    }
    
    private void logState(String info) {
		LOG.debug("state"+(info!=null?" "+info:"")+":");
		LOG.debug("  token:          "+token);
		LOG.debug("  tokenStack:     "+parserState.getTokenStack().toString());
		LOG.debug("  propertyStack:  "+parserState.getPropertyStack().toString());
		LOG.debug("  criterionStack: "+parserState.getOperatorStack().toString());
		LOG.debug("  argumentStack:  "+parserState.getArgumentStack().toString());
		LOG.debug("  parseErrors:    "+parseErrors.toString());
    }
    
    /**
	 * 
	 */
	private void logState() {
		logState(null);
	}

    private void and() throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*and()*");
			logState();
		}
		parserState.begin();
    	match("and");
    	try{
    		property(0);
    		hqlExpression();
    	}
    	catch (RollBackException e){
    		parserState.rollback();
    		parseErrors.add(e.getMessage());
    		throw e;
    	}
		if (LOG.isDebugEnabled()){
			LOG.debug("*and() merging*");
		}
   		parserState.pushOperator(new AndOperator(parserState.popOperator(), parserState.popOperator()));
    }

    private void or() throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*or()*");
			logState();
		}
		parserState.begin();
    	match("or");
    	try{
	    	property(0);
	    	hqlExpression();
    	}
    	catch(RollBackException e){
    		parserState.rollback();
    		parseErrors.add(e.getMessage());
    		throw e;
    	}
		if (LOG.isDebugEnabled()){
			LOG.debug("*or() merging*");
		}
    	parserState.pushOperator(new OrOperator(parserState.popOperator(), parserState.popOperator()));
    }
    
    // Like, LikeLower, LikeUpper
    private void like(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*like("+argumentErrorPrefix+")*");
			logState();
		}
		match("like");
    	if (!parserState.getTokenStack().isEmpty()){
	    	boolean lower = "lower".equalsIgnoreCase(value());
	    	boolean upper = "upper".equalsIgnoreCase(value());
	    	if (lower || upper){
	    		match();
	    		parserState.pushOperator(new ILikeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Like"+(lower?"Lower":"Upper"))));
	    		return;
	    	}
    	}
    	parserState.pushOperator(new LikeOperator(parserState.popProperty(), parserState.popArgument("Like")));
    }
    
    private void ilike(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*ilike("+argumentErrorPrefix+")*");
			logState();
		}
		match("ilike");
		parserState.pushOperator(new ILikeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Ilike")));
    }

    private void i_like(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*i_like("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
		match("i");
    	if ("like".equalsIgnoreCase(value())){
    		match();
    		parserState.pushOperator(new ILikeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"ILike")));
    		return;
    	}
    	parserState.rollback();
    	throw new RollBackException("Missing operator after I: Like (for ILike)");
    }
    
    private void between(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*between("+argumentErrorPrefix+")*");
			logState();
		}

    	match("between");
    	parserState.pushOperator(new BetweenOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Between low"), parserState.popArgument(argumentErrorPrefix+"Between hi")));
    }
    
    private void gt(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*gt("+argumentErrorPrefix+")*");
			logState();
		}

    	match("gt");
    	parserState.pushOperator(new GtOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Gt")));
    }
    
    private void ge(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*ge("+argumentErrorPrefix+")*");
			logState();
		}
    	match("ge");
    	parserState.pushOperator(new GeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Ge")));
    }
    
    private void lt(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*lt("+argumentErrorPrefix+")*");
			logState();
		}
		
    	match("lt");
    	parserState.pushOperator(new LtOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Lt")));
    }

    private void le(String argumentErrorPrefix){
		if (LOG.isDebugEnabled()){
			LOG.debug("*le("+argumentErrorPrefix+")*");
			logState();
		}
		
    	match("le");
    	parserState.pushOperator(new LeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"Le")));
    }
    
    private void is() throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*is()*");
			logState();
		}
		parserState.begin();
		match("is");
		try{
	    	if ("not".equalsIgnoreCase(value())){
	    		is_not_null();
	    		return;
	    	}
	    	else if ("null".equalsIgnoreCase(value())){
	    		is_null();
	    		return;
	    	}
		}
		catch(RollBackException e){
			parserState.rollback();
			parseErrors.add(e.getMessage());
			throw e;
		}
		parserState.rollback();
		throw new RollBackException("Missing operator after Is: Not or Null (for IsNull / IsNotNull)");
    }
    
    private void is_null(){
		if (LOG.isDebugEnabled()){
			LOG.debug("*is_null()*");
			logState();
		}

		match("null");
		parserState.pushOperator(new IsNullOperator(parserState.popProperty()));
    }
    
    private void is_not_null() throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*is_not_null()*");
			logState();
		}
		parserState.begin();
    	match("not");
    	if ("null".equalsIgnoreCase(value())){
    		match();
    		parserState.pushOperator(new IsNotNullOperator(parserState.popProperty()));
    		return;
    	}

    	parserState.rollback();
    	throw new RollBackException("Missing operator after Not: Null (for IsNotNull)");
    }
    
    private void isnull(){
		if (LOG.isDebugEnabled()){
			LOG.debug("*isnull()*");
			logState();
		}
		match("isnull");
		parserState.pushOperator(new IsNullOperator(parserState.popProperty()));
    }
    
    private void isnotnull(){
		if (LOG.isDebugEnabled()){
			LOG.debug("*isnotnull()*");
			logState();
		}
		match("isnotnull");
		parserState.pushOperator(new IsNotNullOperator(parserState.popProperty()));
    }
    
    private void greater(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*greater("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
		match("greater");
		try{
	    	if ("than".equalsIgnoreCase(value())){
	    		greater_than(argumentErrorPrefix);
	    		return;
	    	}
		}
		catch(RollBackException e){
			parserState.rollback();
			parseErrors.add(e.getMessage());
			throw e;
		}
		parserState.rollback();
		throw new RollBackException("Missing operator after Greater: Than (for GreaterThan / GreaterThanOrEqual)");
    }
    
    private void greater_than(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*greater_than("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
		match("than");
		try{
	    	if ("or".equalsIgnoreCase(value())){
	    		greater_than_or_equal(argumentErrorPrefix);
	    		return;
	    	}
		}
		catch(RollBackException e){
			parserState.rollback();
			parseErrors.add(e.getMessage());
			throw e;
		}
    	parserState.pushOperator(new GtOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"GreaterThan")));
    }
    
    private void greater_than_or_equal(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*greater_than_or_equal("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
		match("or");
    	if ("equal".equalsIgnoreCase(value())){
    		match();
    		parserState.pushOperator(new GeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"GreaterThanOrEqual")));
    		return;
    	}
    	parserState.rollback();
    	throw new RollBackException("Missing operator after Or: Equal (for GreaterThanOrEqual)");
    }
    
    private void less(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*less("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
    	match("less");
    	try{
	    	if ("than".equalsIgnoreCase(value())){
	    		less_than(argumentErrorPrefix);
	    		return;
	    	}
    	}
    	catch(RollBackException e){
    		parserState.rollback();
    		parseErrors.add(e.getMessage());
    		throw e;
    	}
    	parserState.rollback();
    	throw new RollBackException("Missing operator after Less: Than (for LessThan)");
    }
    
    private void less_than(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*less_than("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
		match("less");
		try{
	    	if ("or".equalsIgnoreCase(value())){
	    		less_than_or_equal(argumentErrorPrefix);
	    		return;
	    	}
		}
		catch(RollBackException e){
			parserState.rollback();
			parseErrors.add(e.getMessage());
			throw e;
		}
    	parserState.pushOperator(new LtOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"LessThan")));
    }
    
    private void less_than_or_equal(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*less_than_or_equal("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
    	match("or");
    	if ("equal".equalsIgnoreCase(value())){
    		parserState.pushOperator(new LeOperator(parserState.popProperty(), parserState.popArgument(argumentErrorPrefix+"LessThanOrEqual")));
    		return;
    	}
    	parserState.rollback();
    	throw new RollBackException("Missing operator after Or: Equal (for LessThanOrEqual)");
    }
    
    private void ne(){
		if (LOG.isDebugEnabled()){
			LOG.debug("*ne()*");
			logState();
		}
		
    	match("ne");
    	parserState.pushOperator(new NeOperator(parserState.popProperty(), parserState.popArgument("Ne")));
    }
    
    private void not(String argumentErrorPrefix) throws RollBackException{
		if (LOG.isDebugEnabled()){
			LOG.debug("*not("+argumentErrorPrefix+")*");
			logState();
		}
		parserState.begin();
    	match("not");
    	// Not
    	// NotLike, NotILike, NotIlike, NotGreaterThan.., NotLessThan..., NotEqual (Ne), NotBetween,
		final String value = value().toLowerCase();
		try{
	    	if ("like".equals(value)){
	    		like(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Ilike
	    	else if ("ilike".equals(value)){
	    		ilike(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// I
	    	// ILike
	    	else if("i".equals(value)){
	    		i_like(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Between
	    	else if ("between".equals(value)){
	    		between(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Gt
	    	else if ("gt".equals(value)){
	    		gt(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Lt
	    	else if ("lt".equals(value)){
	    		lt(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Le
	    	else if ("le".equals(value)){
	    		le(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// Ge
	    	else if ("ge".equals(value)){
	    		ge(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// GreaterThan, GreaterThanOrEqual
	    	else if ("greater".equals(value)){
	    		greater(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
	    	// LessThan, LessThanOrEqual
	    	else if ("less".equals(value)){
	    		less(argumentErrorPrefix+"Not");
	    		parserState.pushOperator(new NotOperator(parserState.popOperator()));
	    		return;
	    	}
		}
		catch(RollBackException e){
			parserState.rollback();
			parseErrors.add(e.getMessage());
			throw e;
		}
		parserState.rollback();
		throw new RollBackException("Missing one of operators after Not: Like, Ilike, I, Between, Gt, Lt, Le, Ge, Greater, Less");
    }
}
